package com.androiddatasender;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;

public class Net
{
    private static Net instance = new Net();
    private ReceiveCallback callback = null;
    private Configuration conf = null;
    private Socket sck = null;
    private ServerSocket svr = null;
    private DataOutputStream dos = null;
    private Receiver receiver = null;
    private boolean running = true;
    private ArrayList<Receiver> receiverList = null;

    private Net()
    {
    }

    public static Net getInstance()
    {
        return instance;
    }

    public void initialize(Configuration conf)
    {
        this.conf = conf;
        if (conf.transport == Configuration.TRANSPORT_TCP)
        {
            if (conf.mode == Configuration.MODE_SENDER)
            {
                try
                {
                    sck = new Socket();
                    sck.connect(new InetSocketAddress(conf.ip, conf.port));
                    dos = new DataOutputStream(sck.getOutputStream());
                    receiver = new Receiver(sck, this.callback);
                    receiver.start();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            else if (conf.mode == Configuration.MODE_RECEIVER)
            {
                try
                {
                    receiverList = new ArrayList<Receiver>();
                    svr = new ServerSocket();
                    svr.bind(new InetSocketAddress(conf.ip, conf.port));
                } catch (IOException ioe)
                {
                }
            }
        }
        else
        {
        }
    }

    public void connect()
    {
        if (conf.transport == Configuration.TRANSPORT_TCP)
        {
            if (conf.mode == Configuration.MODE_SENDER)
            {
                try
                {
                    sck = new Socket();
                    boolean tryAgain = true;
                    while (tryAgain)
                    {
                        try
                        {
                            sck.connect(new InetSocketAddress(conf.ip, conf.port), 2000);
                            tryAgain = false;
                        } catch (SocketTimeoutException ste)
                        {
                            ste.printStackTrace();
                            try
                            {
                              Thread.sleep(1000);
                            }
                            catch (InterruptedException ire)
                            {}
                        } catch (SocketException se)
                        {
                            sck = new Socket();
                            se.printStackTrace();
                            try
                            {
                                Thread.sleep(1000);
                            }
                            catch (InterruptedException ire)
                            {}
                        }
                    }
                    dos = new DataOutputStream(sck.getOutputStream());
                    receiver = new Receiver(sck, this.callback);
                    receiver.start();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    public void loop()
    {
        if (conf.mode == Configuration.MODE_SENDER)
        {
            while (running)
            {
                try
                {
                    Thread.sleep(1000);
                } catch (InterruptedException ire)
                {
                }
            }
        }
        else if (conf.mode == Configuration.MODE_RECEIVER)
        {
            while (running)
            {
                try
                {
                    Socket sck = svr.accept();
                    this.sck = sck;
                    if (this.callback != null)
                    {
                        receiver = new Receiver(sck, this.callback);
                        receiver.start();
                        receiverList.add(receiver);
                    }
                } catch (IOException ioe)
                {

                }
            }
        }
    }

    public void shutdown()
    {
        try
        {
            running = false;

            if (receiver != null) receiver.shutdown();

            while (receiverList.size() > 0)
            {
                Receiver receiver = receiverList.get(0);
                receiverList.remove(0);
                receiver.shutdown();
            }

            if (sck != null) sck.close();
            if (svr != null) svr.close();
        } catch (IOException e)
        {
        }
    }

    public ArrayList<Receiver> getReceiverList()
    {
        return receiverList;
    }

    public void send(Msg msg)
    {
        if (conf.mode == Configuration.MODE_SENDER)
        {
            try
            {
                byte[] buffer = msg.packet.toByteArray();
                dos.write(buffer, 0, buffer.length);
                dos.flush();
            } catch (IOException e)
            {
            }
        }
        else if (conf.mode == Configuration.MODE_RECEIVER)
        { // BroadCast
            for (Receiver receiver : receiverList)
            {
                receiver.send(msg);
            }
        }
    }

    public void receiveCallback(ReceiveCallback callback)
    {
        this.callback = callback;
    }
}
